#ifndef SEM_V1_HPP
#define SEM_V1_HPP

#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define EXIT_ERROR(m) \
    do                \
    {                 \
        perror(m);    \
        exit(1);      \
    } while (0)

class Sem
{
public:
    Sem(int semid) : _semid(semid)
    {
    }
    ~Sem()
    {
        int ret = semctl(_semid, 0, IPC_RMID);
        if (ret < 0)
        {
            EXIT_ERROR("semctl");
        }
        std::cout << "~Sem() success!" << std::endl;
    }
    void P()
    {
        struct sembuf buf;
        buf.sem_num = 0;
        buf.sem_op = -1;
        buf.sem_flg = SEM_UNDO;
        int ret = semop(_semid, &buf, 1);
        if (ret < 0)
        {
            EXIT_ERROR("semop");
        }
    }
    void V()
    {
        struct sembuf buf;
        buf.sem_num = 0;
        buf.sem_op = 1;
        buf.sem_flg = SEM_UNDO;
        int ret = semop(_semid, &buf, 1);
        if (ret < 0)
        {
            EXIT_ERROR("semop");
        }
    }

private:
    int _semid;
};

#define SEM_CREAT (IPC_CREAT | IPC_EXCL | 0666)
#define SEM_GET (IPC_CREAT)

const std::string path_default = ".";
const int proj_id_default = 0x11;

class SemBuilder
{
public:
    SemBuilder()
    {
    }
    ~SemBuilder()
    {
    }
    SemBuilder *SetVal(int val)
    {
        _val = val;
        return this;
    }

    std::shared_ptr<Sem> Build(int flag)
    {
        // 1.创建/获取信号量集合
        key_t key = ftok(path_default.c_str(), proj_id_default);
        int semid = semget(key, 1, flag);
        if (semid < 0)
        {
            EXIT_ERROR("semget");
        }
        std::cout << "sem create success! semid:" << semid << std::endl;
        // 2.只有父进程要初始化信号量集合
        if (flag == SEM_CREAT)
        {
            Init(semid);
            std::cout << "Init success" << std::endl;
        }
        // 3.返回Sem对象
        std::shared_ptr<Sem> sp = std::make_shared<Sem>(semid);
        return sp;
    }

private:
    void Init(int semid)
    {
        union semun
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                      (Linux-specific) */
        } sun;
        sun.val = _val;
        int ret = semctl(semid, 0, SETVAL, sun);
        if (ret < 0)
        {
            EXIT_ERROR("semctl");
        }
    }

private:
    int _val;
};

#endif // SEM_V1_HPP